Introduction
This webpage shows some R code for generating figures for Mendelian randomization analyses.
First, we load in the tidyverse collection of packages to have access to the pipe and the ggplot2 package, amongst other features.
library(tidyverse)
If you do not already one you will need a LaTeX installation. In R it is easiest to run install TinyTeX as follows.
install.packages("tinytex")
tinytex::install_tinytex()
DiagrammeR package
The website for DiagrammeR is here: http://rich-iannone.github.io/DiagrammeR/
Single genotype DAG
DiagrammeR::grViz("
digraph mrdag {
graph [rankdir=TB]
node [shape=ellipse]
U [label='Confounders']
node [shape=box, height=0.3, width=0.3]
G [label='Genotype']
X [label='Phenotype']
Y [label='Outcome']
{ rank = same; G X Y }
G -> X [minlen=3]
U -> X
U -> Y
X -> Y [minlen=3]
}
", height = 200)
Multiple genotype DAG
DiagrammeR::grViz("
digraph mrdag {
graph [rankdir=TB, layout=neato]
node [shape=ellipse]
U [label='Confounders', pos='3,1!']
node [shape=box, height=0.3, width=0.3]
G1 [label='G@_{1}', pos='0,0.75!']
G2 [label='G@_{2}', pos='0,0!']
G3 [label='G@_{3}', pos='0,-0.75!']
X [label='Phenotype', pos='2,0!']
Y [label='Outcome', pos='4,0!']
{ rank = same; G2 X Y }
G1 -> X
G2 -> X
G3 -> X
U -> X
U -> Y
X -> Y
}
", height = 200)
This could be extended for more genotypes.
Single genotype path diagram
Explicitly including the confounder, \(U\).
DiagrammeR::grViz("
digraph mrdag {
graph [rankdir=TB, layout=neato]
node [shape=ellipse, height=0.3, width=0.3]
U [pos='3,1!']
node [shape=box, height=0.3, width=0.3]
G [pos='0,0!']
X [pos='2,0!']
Y [pos='4,0!']
node [shape=circle, height=0.35, fixedsize=true]
Ex [label='ε@_{X}', pos='2,1!']
Ey [label='ε@_{Y}', pos='4,1!']
G -> X [label='β@_{GX}']
Ex -> X [label=1]
Ey -> Y [label=1]
U -> X [label='β@_{UX}']
U -> Y [label='β@_{UY}']
X -> Y [label='β@_{XY}']
U -> U [dir='both', headport='n', tailport='n']
G -> G [dir='both', headport='w', tailport='w']
}
", height = 200)
Instead drawing correlated error terms between \(X\) and \(Y\).
DiagrammeR::grViz("
digraph mrdag {
graph [rankdir=TB, layout=neato]
node [shape=box, height=0.3, width=0.3]
G [pos='0,0!']
X [pos='2,0!']
Y [pos='4,0!']
node [shape=circle, height=0.35, fixedsize=true]
Ex [label='ε@_{X}', pos='2,1!']
Ey [label='ε@_{Y}', pos='4,1!']
G -> X [label='β@_{GX}']
Ex -> X [label=1]
Ey -> Y [label=1]
X -> Y [label='β@_{XY}']
Ex -> Ey [dir='both', label='ρ']
G -> G [dir='both', headport='w', tailport='w']
}
", height = 200)
dagitty package
The website for DAGitty is here http://www.dagitty.net/
library(dagitty)
Using dot syntax
The DAG for a single genotype.
mrdag <- dagitty('dag {
G -> X -> Y
X <- U -> Y
G [pos="0,0"]
X [e, pos="1,0"]
U [pos="1.5,-1"]
Y [o, pos="2,0"]
}')
plot(mrdag)

Using ggdag
library(ggdag)
mrdag <- dagitty('dag {
G -> X -> Y
X <- U -> Y
G [pos="0,0"]
X [e, pos="1,0"]
U [pos="1.5,1"]
Y [o, pos="2,0"]
}')
ggmrdag <- tidy_dagitty(mrdag)
ggdag(ggmrdag)

We can position U above X.
mrdag <- dagitty('dag {
G -> X -> Y
X <- U -> Y
G [pos="0,0"]
X [e, pos="1,0"]
U [pos="1,1"]
Y [o, pos="2,0"]
}')
ggmrdag <- tidy_dagitty(mrdag)
ggdag(ggmrdag)

This can be labelled as follows.
mrdag <- mrdag %>%
dag_label(
labels = c(
"X" = "Exposure",
"Y" = "Outcome",
"G" = "Genotype",
"U" = "Confounders"))
ggdag(mrdag, use_labels = "label")

Dagitty can identify the instrumental variable on the DAG.
mrdag %>% ggdag_instrumental()

It correctly identifies the genotype, G, as the instrumental variable.
Using R model type syntax in ggdag
coords <- list(x = c(
G = 0,
X = 1,
Y = 2,
U = 1.5
),
y = c(
G = 0,
X = 0,
Y = 0,
U = 1
))
mrdag <- dagify(
X ~ G + U,
Y ~ X + U,
labels = c(
"G" = "Genotype",
"X" = "Exposure",
"Y" = "Outcome",
"U" = "Confounders"
),
exposure = "X",
outcome = "Y",
coords = coords
)
ggdag(mrdag, use_labels = "label")

Plotting directly with ggplot2 functions.
mrdag %>%
ggplot(aes(
x = x,
y = y,
xend = xend,
yend = yend
)) +
geom_dag_point() +
geom_dag_edges() +
geom_dag_text() +
theme_dag()

Showing the paths.
dagify(Y ~ X + U,
X ~ U + G,
exposure = "X",
outcome = "Y") %>%
ggdag_paths()

Adjustment set for the effect of \(X\) on \(Y\).
dagify(Y ~ X + U,
X ~ U + G,
exposure = "X",
outcome = "Y",
coords = coords) %>%
ggdag_adjustment_set()

This shows that the path from G to X is unconfounded. And that when U is adjusted for the path from X to Y is unconfounded.
Multiple genotype DAG
coords2 <- list(x = c(
G1 = 0,
G2 = 0,
G3 = 0,
X = 1,
Y = 2,
U = 1.5
),
y = c(
G1 = 0.5,
G2 = 0,
G3 = -0.5,
X = 0,
Y = 0,
U = 1
))
mrdag <- dagify(
X ~ G1,
X ~ G2,
X ~ G3,
Y ~ X,
X ~ U,
Y ~ U,
exposure = "X",
outcome = "Y",
coords = coords2
)
ggdag(mrdag)

This could be extended for more genotypes.
Eleanor Murray’s TikZ diagrams using the tikz code chunk engine
I believe that tikz diagrams can be included in Rmarkdown documents using the tikz code chunk engine for output types pdf_document and html_document.
Tikz is a package for LaTeX and so pdf is its natural output. So far I have not been able to include these figures in either html_notebook or word_document output formats. I suspect that the images need conversion to png files for these to work under rmarkdown.
We will use some of the LaTeX code for drawing DAGs using tikz provided by Eleanor Murray here.
In its LaTeX preamble, this code loads in several tikz libraries. As such for our tikz code chunks to compile in our Rmd file we need to define a new template file, e.g.tikz-template.tex, and call our tikz code chunks as follows.
```{tikz, fig.ext='svg', fig.width=3, engine.opts=list(template = "tikz-template.tex")}
\begin{tikzpicture}
...
\end{tikzpicture}
```
Note that I only include the fig.ext='svg' chunk option for html output. The tikz-template.tex file includes the tikz preamble code from Eleanor Murray’s LaTeX code plus the required template code. The contents of the template file is as follows.
\documentclass{article}
\usepackage[active,tightpage]{preview}
\usepackage{amsmath}
\usepackage{tikz}
\usetikzlibrary{positioning, calc, shapes.geometric, shapes.multipart,
shapes, arrows.meta, arrows,decorations.markings, external, trees}
% Create custom arrow style:
\tikzstyle{Arrow} = [
thick,
decoration={
markings,
mark=at position 1 with {
\arrow[thick]{latex}
}
},
shorten >= 3pt, preaction = {decorate}
]
\begin{document}
\begin{preview}
%% TIKZ_CODE %%
\end{preview}
\end{document}
We can now plot our DAGs.
Aside: I should note that I could not get this code to run on my MacBook. This is because by default the dvisvg executable on MacOS appears to be missing libgs support. It is possible to compile dvisvg from source including libgs support but I did not try this.
Single genotype DAG using tikz
\begin{tikzpicture}
\node (1) at (0,0) {G};
\node (2) at (1.5,0) {X};
\node (3) at (3,0) {Y};
\node (4) at (2.25,1) {U};
\draw[Arrow] (1.east) -- (2.west);
\draw[Arrow] (2.east) -- (3.west);
\draw[Arrow] (4) -- (2);
\draw[Arrow] (4) -- (3);
\end{tikzpicture}

Multiple genotype DAG using tikz
\begin{tikzpicture}
\node (1) at (0,0) {$G_2$};
\node (2) at (1.5,0) {X};
\node (3) at (3,0) {Y};
\node (4) at (2.25,1) {U};
\node (5) at (0,0.5) {$G_1$};
\node (6) at (0,-0.5) {$G_3$};
\draw[Arrow] (1.east) -- (2.west);
\draw[Arrow] (2.east) -- (3.west);
\draw[Arrow] (4) -- (2);
\draw[Arrow] (4) -- (3);
\draw[Arrow] (5) -- (2);
\draw[Arrow] (6) -- (2);
\end{tikzpicture}

Again this could be extended for more genotypes.
quickdag package
We can produce some nice plots with the quickdag package, which appears to be based on Eleanor Murray’s TikZ code, from here
# remotes::install_github("jrgant/quickdag")
library(quickdag)
## Loading required package: DiagrammeR
edges <- c("G -> X",
"X -> Y",
"U -> {X Y}")
# DAG
dag <- qd_dag(edges)
##
##
## CHECKED: The diagram is a DAG.
## -------------------------------------------------------------------------
## Make sure everything is matched up properly!
## To stop printing data to the console, set 'verbose' to FALSE.
## -------------------------------------------------------------------------
## ........................ NODE DATAFRAME ........................
##
## id type label alpha.id
## 1 1 <NA> G G
## 2 2 <NA> U U
## 3 3 <NA> X X
## 4 4 <NA> Y Y
##
## ........................ EDGE DATAFRAME ........................
##
## id from to rel
## 1 1 1 3 <NA>
## 2 2 3 4 <NA>
## 3 3 2 3 <NA>
## 4 4 2 4 <NA>
## Warning: `data_frame()` is deprecated as of tibble 1.1.0.
## Please use `tibble()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.
dag %>% render_graph()
We can also draw the single world intervention graph (SWIG) for the IV model as follow.
# SWiG
swig <- dag %>%
qd_swig(fixed.nodes = c("G","X"),
custom.labels = c("G" = "g", "X" = "x"))
swig %>% render_graph()
Thomas Richardson’s TikZ code for SWIGs
Thomas Richardson provides a TikZ/PGF shape library for SWIGs.
To use the tikz chunk engine we again write a LaTeX template based on the preamble in the article by Richardson. The LaTeX template is as follows.
\documentclass{article}
\usepackage[active,tightpage]{preview}
\usepackage{amsmath}
\usepackage{pgf,tikz}
\usetikzlibrary{arrows,shapes.arrows,shapes.geometric,shapes.multipart,
decorations.pathmorphing,positioning,shapes.swigs}
\begin{document}
\begin{preview}
%% TIKZ_CODE %%
\end{preview}
\end{document}
We will recreate the SWIG template in Figure 2 (b) of Swanson et al. here.
\begin{tikzpicture}
\tikzset{line width=1.5pt, outer sep=0pt,
ell/.style={draw,fill=white, inner sep=2pt,
line width=1.5pt},
swig vsplit={gap=5pt, inner line width right=0.5pt},
swig hsplit={gap=5pt, inner line width lower=0.5pt}};
\node[name=G,shape=swig vsplit]{
\nodepart{left}{$G$}
\nodepart{right}{$g$}};
\node[name=X, right=10mm of G,
shape=swig hsplit]{
\nodepart{upper}{$X^g$}
\nodepart{lower}{$x$}};
\node[name=U, ell, shape=ellipse, above right=7.5mm and 5mm of X, fill=gray]{$U$};
\node[name=Y, ell, shape=ellipse, right=10mm of X]{$Y^x$};
\draw[->, line width=1.5pt,>=stealth]
(G) edge[out=360, in=170] (X)
(U) edge (X)
(U) edge (Y)
(X) to[out=330, in=180] (Y);
\end{tikzpicture}

LS0tDQp0aXRsZTogIlIgYW5kIFRpa1ogY29kZSB0byBwbG90IERBR3MgYW5kIFNXSUdzIGZvciBNZW5kZWxpYW4gcmFuZG9taXphdGlvbiBhbmFseXNlcyINCmF1dGhvcjogIlRvbSBQYWxtZXIiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAyDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgIGRldjogc3ZnDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KLS0tDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpyZXF1aXJlKCJIbWlzYyIpDQpgYGANCmByIEhtaXNjOjpoaWRpbmdUT0MobGV2ZWxzID0gMilgDQoNCiMgSW50cm9kdWN0aW9uDQoNClRoaXMgd2VicGFnZSBzaG93cyBzb21lIFIgY29kZSBmb3IgZ2VuZXJhdGluZyBmaWd1cmVzIGZvciBNZW5kZWxpYW4gcmFuZG9taXphdGlvbiBhbmFseXNlcy4NCg0KRmlyc3QsIHdlIGxvYWQgaW4gdGhlIHRpZHl2ZXJzZSBjb2xsZWN0aW9uIG9mIHBhY2thZ2VzIHRvIGhhdmUgYWNjZXNzIHRvIHRoZSBwaXBlIGFuZCB0aGUgZ2dwbG90MiBwYWNrYWdlLCBhbW9uZ3N0IG90aGVyIGZlYXR1cmVzLg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQpJZiB5b3UgZG8gbm90IGFscmVhZHkgb25lIHlvdSB3aWxsIG5lZWQgYSBMYVRlWCBpbnN0YWxsYXRpb24uIEluIFIgaXQgaXMgZWFzaWVzdCB0byBydW4gaW5zdGFsbCBUaW55VGVYIGFzIGZvbGxvd3MuDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmluc3RhbGwucGFja2FnZXMoInRpbnl0ZXgiKQ0KdGlueXRleDo6aW5zdGFsbF90aW55dGV4KCkNCmBgYA0KDQojIERpYWdyYW1tZVIgcGFja2FnZQ0KDQpUaGUgd2Vic2l0ZSBmb3IgRGlhZ3JhbW1lUiBpcyBoZXJlOiBodHRwOi8vcmljaC1pYW5ub25lLmdpdGh1Yi5pby9EaWFncmFtbWVSLw0KDQoNCiMjIFNpbmdsZSBnZW5vdHlwZSBEQUcNCg0KYGBge3J9DQpEaWFncmFtbWVSOjpnclZpeigiDQogICAgICBkaWdyYXBoIG1yZGFnIHsNCg0KICAgICAgZ3JhcGggW3JhbmtkaXI9VEJdDQoNCiAgICAgIG5vZGUgW3NoYXBlPWVsbGlwc2VdDQogICAgICBVIFtsYWJlbD0nQ29uZm91bmRlcnMnXQ0KDQogICAgICBub2RlIFtzaGFwZT1ib3gsIGhlaWdodD0wLjMsIHdpZHRoPTAuM10NCiAgICAgIEcgW2xhYmVsPSdHZW5vdHlwZSddDQogICAgICBYIFtsYWJlbD0nUGhlbm90eXBlJ10NCiAgICAgIFkgW2xhYmVsPSdPdXRjb21lJ10NCg0KICAgICAgeyByYW5rID0gc2FtZTsgRyBYIFkgfQ0KDQogICAgICBHIC0+IFggW21pbmxlbj0zXQ0KICAgICAgVSAtPiBYDQogICAgICBVIC0+IFkNCiAgICAgIFggLT4gWSBbbWlubGVuPTNdDQogICAgICB9DQogICAgICAiLCBoZWlnaHQgPSAyMDApDQpgYGANCg0KIyMgTXVsdGlwbGUgZ2Vub3R5cGUgREFHDQoNCmBgYHtyfQ0KRGlhZ3JhbW1lUjo6Z3JWaXooIg0KICAgICAgZGlncmFwaCBtcmRhZyB7DQoNCiAgICAgIGdyYXBoIFtyYW5rZGlyPVRCLCBsYXlvdXQ9bmVhdG9dDQoNCiAgICAgIG5vZGUgW3NoYXBlPWVsbGlwc2VdDQogICAgICBVIFtsYWJlbD0nQ29uZm91bmRlcnMnLCBwb3M9JzMsMSEnXQ0KDQogICAgICBub2RlIFtzaGFwZT1ib3gsIGhlaWdodD0wLjMsIHdpZHRoPTAuM10NCiAgICAgIEcxIFtsYWJlbD0nR0BfezF9JywgcG9zPScwLDAuNzUhJ10NCiAgICAgIEcyIFtsYWJlbD0nR0BfezJ9JywgcG9zPScwLDAhJ10NCiAgICAgIEczIFtsYWJlbD0nR0BfezN9JywgcG9zPScwLC0wLjc1ISddDQogICAgICBYIFtsYWJlbD0nUGhlbm90eXBlJywgcG9zPScyLDAhJ10NCiAgICAgIFkgW2xhYmVsPSdPdXRjb21lJywgcG9zPSc0LDAhJ10NCg0KICAgICAgeyByYW5rID0gc2FtZTsgRzIgWCBZIH0NCg0KICAgICAgRzEgLT4gWCANCiAgICAgIEcyIC0+IFggDQogICAgICBHMyAtPiBYIA0KICAgICAgVSAtPiBYDQogICAgICBVIC0+IFkNCiAgICAgIFggLT4gWSANCiAgICAgIH0NCiAgICAgICIsIGhlaWdodCA9IDIwMCkNCmBgYA0KDQpUaGlzIGNvdWxkIGJlIGV4dGVuZGVkIGZvciBtb3JlIGdlbm90eXBlcy4NCg0KIyMgU2luZ2xlIGdlbm90eXBlIHBhdGggZGlhZ3JhbQ0KDQpFeHBsaWNpdGx5IGluY2x1ZGluZyB0aGUgY29uZm91bmRlciwgJFUkLg0KYGBge3J9DQpEaWFncmFtbWVSOjpnclZpeigiDQogICAgICBkaWdyYXBoIG1yZGFnIHsNCg0KICAgICAgZ3JhcGggW3JhbmtkaXI9VEIsIGxheW91dD1uZWF0b10NCg0KICAgICAgbm9kZSBbc2hhcGU9ZWxsaXBzZSwgaGVpZ2h0PTAuMywgd2lkdGg9MC4zXQ0KICAgICAgVSBbcG9zPSczLDEhJ10NCg0KICAgICAgbm9kZSBbc2hhcGU9Ym94LCBoZWlnaHQ9MC4zLCB3aWR0aD0wLjNdDQogICAgICBHIFtwb3M9JzAsMCEnXQ0KICAgICAgWCBbcG9zPScyLDAhJ10NCiAgICAgIFkgW3Bvcz0nNCwwISddDQoNCiAgICAgIG5vZGUgW3NoYXBlPWNpcmNsZSwgaGVpZ2h0PTAuMzUsIGZpeGVkc2l6ZT10cnVlXQ0KICAgICAgRXggW2xhYmVsPScmZXBzaWxvbjtAX3tYfScsIHBvcz0nMiwxISddDQogICAgICBFeSBbbGFiZWw9JyZlcHNpbG9uO0Bfe1l9JywgcG9zPSc0LDEhJ10NCg0KICAgICAgRyAtPiBYIFtsYWJlbD0nJmJldGE7QF97R1h9J10NCiAgICAgIEV4IC0+IFggW2xhYmVsPTFdDQogICAgICBFeSAtPiBZIFtsYWJlbD0xXQ0KICAgICAgVSAtPiBYIFtsYWJlbD0nJmJldGE7QF97VVh9J10NCiAgICAgIFUgLT4gWSBbbGFiZWw9JyZiZXRhO0Bfe1VZfSddDQogICAgICBYIC0+IFkgW2xhYmVsPScmYmV0YTtAX3tYWX0nXQ0KICAgICAgVSAtPiBVIFtkaXI9J2JvdGgnLCBoZWFkcG9ydD0nbicsIHRhaWxwb3J0PSduJ10NCiAgICAgIEcgLT4gRyBbZGlyPSdib3RoJywgaGVhZHBvcnQ9J3cnLCB0YWlscG9ydD0ndyddDQogICAgICB9DQogICAgICAiLCBoZWlnaHQgPSAyMDApDQpgYGANCg0KSW5zdGVhZCBkcmF3aW5nIGNvcnJlbGF0ZWQgZXJyb3IgdGVybXMgYmV0d2VlbiAkWCQgYW5kICRZJC4NCg0KYGBge3J9DQpEaWFncmFtbWVSOjpnclZpeigiDQogICAgICBkaWdyYXBoIG1yZGFnIHsNCg0KICAgICAgZ3JhcGggW3JhbmtkaXI9VEIsIGxheW91dD1uZWF0b10NCg0KICAgICAgbm9kZSBbc2hhcGU9Ym94LCBoZWlnaHQ9MC4zLCB3aWR0aD0wLjNdDQogICAgICBHIFtwb3M9JzAsMCEnXQ0KICAgICAgWCBbcG9zPScyLDAhJ10NCiAgICAgIFkgW3Bvcz0nNCwwISddDQoNCiAgICAgIG5vZGUgW3NoYXBlPWNpcmNsZSwgaGVpZ2h0PTAuMzUsIGZpeGVkc2l6ZT10cnVlXQ0KICAgICAgRXggW2xhYmVsPScmZXBzaWxvbjtAX3tYfScsIHBvcz0nMiwxISddDQogICAgICBFeSBbbGFiZWw9JyZlcHNpbG9uO0Bfe1l9JywgcG9zPSc0LDEhJ10NCg0KICAgICAgRyAtPiBYIFtsYWJlbD0nJmJldGE7QF97R1h9J10NCiAgICAgIEV4IC0+IFggW2xhYmVsPTFdDQogICAgICBFeSAtPiBZIFtsYWJlbD0xXQ0KICAgICAgWCAtPiBZIFtsYWJlbD0nJmJldGE7QF97WFl9J10NCiAgICAgIEV4IC0+IEV5IFtkaXI9J2JvdGgnLCBsYWJlbD0nJnJobzsnXQ0KICAgICAgRyAtPiBHIFtkaXI9J2JvdGgnLCBoZWFkcG9ydD0ndycsIHRhaWxwb3J0PSd3J10NCiAgICAgIH0NCiAgICAgICIsIGhlaWdodCA9IDIwMCkNCmBgYA0KDQojIGRhZ2l0dHkgcGFja2FnZQ0KDQpUaGUgd2Vic2l0ZSBmb3IgREFHaXR0eSBpcyBoZXJlIGh0dHA6Ly93d3cuZGFnaXR0eS5uZXQvDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShkYWdpdHR5KQ0KYGBgDQoNCiMjIFVzaW5nIGRvdCBzeW50YXgNCg0KVGhlIERBRyBmb3IgYSBzaW5nbGUgZ2Vub3R5cGUuDQpgYGB7cn0NCm1yZGFnIDwtIGRhZ2l0dHkoJ2RhZyB7DQogIEcgLT4gWCAtPiBZDQogIFggPC0gVSAtPiBZDQogIEcgW3Bvcz0iMCwwIl0NCiAgWCBbZSwgcG9zPSIxLDAiXQ0KICBVIFtwb3M9IjEuNSwtMSJdDQogIFkgW28sIHBvcz0iMiwwIl0NCn0nKQ0KDQpwbG90KG1yZGFnKQ0KYGBgDQoNCiMjIFVzaW5nIGdnZGFnDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShnZ2RhZykNCmBgYA0KDQpgYGB7cn0NCm1yZGFnIDwtIGRhZ2l0dHkoJ2RhZyB7DQogIEcgLT4gWCAtPiBZDQogIFggPC0gVSAtPiBZDQogIEcgW3Bvcz0iMCwwIl0NCiAgWCBbZSwgcG9zPSIxLDAiXQ0KICBVIFtwb3M9IjEuNSwxIl0NCiAgWSBbbywgcG9zPSIyLDAiXQ0KfScpDQoNCmdnbXJkYWcgPC0gdGlkeV9kYWdpdHR5KG1yZGFnKQ0KZ2dkYWcoZ2dtcmRhZykNCmBgYA0KDQpXZSBjYW4gcG9zaXRpb24gVSBhYm92ZSBYLg0KDQpgYGB7cn0NCm1yZGFnIDwtIGRhZ2l0dHkoJ2RhZyB7DQogIEcgLT4gWCAtPiBZDQogIFggPC0gVSAtPiBZDQogIEcgW3Bvcz0iMCwwIl0NCiAgWCBbZSwgcG9zPSIxLDAiXQ0KICBVIFtwb3M9IjEsMSJdDQogIFkgW28sIHBvcz0iMiwwIl0NCn0nKQ0KDQpnZ21yZGFnIDwtIHRpZHlfZGFnaXR0eShtcmRhZykNCmdnZGFnKGdnbXJkYWcpDQpgYGANCg0KVGhpcyBjYW4gYmUgbGFiZWxsZWQgYXMgZm9sbG93cy4NCg0KYGBge3J9DQptcmRhZyA8LSBtcmRhZyAlPiUgDQogIGRhZ19sYWJlbCgNCiAgICBsYWJlbHMgPSBjKA0KICAgICAgIlgiID0gIkV4cG9zdXJlIiwgDQogICAgICAiWSIgPSAiT3V0Y29tZSIsIA0KICAgICAgIkciID0gIkdlbm90eXBlIiwNCiAgICAgICJVIiA9ICJDb25mb3VuZGVycyIpKQ0KZ2dkYWcobXJkYWcsICB1c2VfbGFiZWxzID0gImxhYmVsIikNCmBgYA0KDQpEYWdpdHR5IGNhbiBpZGVudGlmeSB0aGUgaW5zdHJ1bWVudGFsIHZhcmlhYmxlIG9uIHRoZSBEQUcuDQoNCmBgYHtyfQ0KbXJkYWcgJT4lIGdnZGFnX2luc3RydW1lbnRhbCgpDQpgYGANCg0KSXQgY29ycmVjdGx5IGlkZW50aWZpZXMgdGhlIGdlbm90eXBlLCBHLCBhcyB0aGUgaW5zdHJ1bWVudGFsIHZhcmlhYmxlLg0KDQojIyBVc2luZyBSIG1vZGVsIHR5cGUgc3ludGF4IGluIGdnZGFnDQoNCmBgYHtyfQ0KY29vcmRzIDwtIGxpc3QoeCA9IGMoDQogIEcgPSAwLA0KICBYID0gMSwNCiAgWSA9IDIsDQogIFUgPSAxLjUNCiksDQp5ID0gYygNCiAgRyA9IDAsDQogIFggPSAwLA0KICBZID0gMCwNCiAgVSA9IDENCikpDQoNCm1yZGFnIDwtIGRhZ2lmeSgNCiAgWCB+IEcgKyBVLA0KICBZIH4gWCArIFUsDQogIGxhYmVscyA9IGMoDQogICAgIkciID0gIkdlbm90eXBlIiwNCiAgICAiWCIgPSAiRXhwb3N1cmUiLA0KICAgICJZIiA9ICJPdXRjb21lIiwNCiAgICAiVSIgPSAiQ29uZm91bmRlcnMiDQogICksDQogIGV4cG9zdXJlID0gIlgiLA0KICBvdXRjb21lID0gIlkiLA0KICBjb29yZHMgPSBjb29yZHMNCikNCg0KZ2dkYWcobXJkYWcsIHVzZV9sYWJlbHMgPSAibGFiZWwiKQ0KYGBgDQoNClBsb3R0aW5nIGRpcmVjdGx5IHdpdGggZ2dwbG90MiBmdW5jdGlvbnMuDQoNCmBgYHtyfQ0KbXJkYWcgJT4lDQogIGdncGxvdChhZXMoDQogICAgeCA9IHgsDQogICAgeSA9IHksDQogICAgeGVuZCA9IHhlbmQsDQogICAgeWVuZCA9IHllbmQNCiAgKSkgKw0KICBnZW9tX2RhZ19wb2ludCgpICsNCiAgZ2VvbV9kYWdfZWRnZXMoKSArDQogIGdlb21fZGFnX3RleHQoKSArDQogIHRoZW1lX2RhZygpDQpgYGANCg0KU2hvd2luZyB0aGUgcGF0aHMuDQoNCmBgYHtyLCBmaWcuZXh0ID0gJ3N2Zyd9DQpkYWdpZnkoWSB+IFggKyBVLA0KICAgICAgIFggfiBVICsgRywNCiAgICAgICBleHBvc3VyZSA9ICJYIiwNCiAgICAgICBvdXRjb21lID0gIlkiKSAlPiUNCmdnZGFnX3BhdGhzKCkNCmBgYA0KDQpBZGp1c3RtZW50IHNldCBmb3IgdGhlIGVmZmVjdCBvZiAkWCQgb24gJFkkLiAgDQoNCmBgYHtyfSAgDQpkYWdpZnkoWSB+IFggKyBVLA0KICAgICAgIFggfiBVICsgRywNCiAgICAgICBleHBvc3VyZSA9ICJYIiwNCiAgICAgICBvdXRjb21lID0gIlkiLCANCiAgICAgICBjb29yZHMgPSBjb29yZHMpICU+JQ0KICBnZ2RhZ19hZGp1c3RtZW50X3NldCgpDQpgYGAgIA0KDQpUaGlzIHNob3dzIHRoYXQgdGhlIHBhdGggZnJvbSBHIHRvIFggaXMgdW5jb25mb3VuZGVkLiBBbmQgdGhhdCB3aGVuIFUgaXMgYWRqdXN0ZWQgZm9yIHRoZSBwYXRoIGZyb20gWCB0byBZIGlzIHVuY29uZm91bmRlZC4NCg0KIyMgTXVsdGlwbGUgZ2Vub3R5cGUgREFHDQoNCmBgYHtyfQ0KY29vcmRzMiA8LSBsaXN0KHggPSBjKA0KICBHMSA9IDAsDQogIEcyID0gMCwNCiAgRzMgPSAwLA0KICBYID0gMSwNCiAgWSA9IDIsDQogIFUgPSAxLjUNCiksDQp5ID0gYygNCiAgRzEgPSAwLjUsDQogIEcyID0gMCwNCiAgRzMgPSAtMC41LA0KICBYID0gMCwNCiAgWSA9IDAsDQogIFUgPSAxDQopKQ0KDQoNCm1yZGFnIDwtIGRhZ2lmeSgNCiAgWCB+IEcxLA0KICBYIH4gRzIsDQogIFggfiBHMywNCiAgWSB+IFgsDQogIFggfiBVLA0KICBZIH4gVSwNCiAgZXhwb3N1cmUgPSAiWCIsDQogIG91dGNvbWUgPSAiWSIsDQogIGNvb3JkcyA9IGNvb3JkczINCikNCg0KZ2dkYWcobXJkYWcpDQpgYGANCg0KVGhpcyBjb3VsZCBiZSBleHRlbmRlZCBmb3IgbW9yZSBnZW5vdHlwZXMuDQoNCiMgRWxlYW5vciBNdXJyYXkncyBUaWtaIGRpYWdyYW1zIHVzaW5nIHRoZSB0aWt6IGNvZGUgY2h1bmsgZW5naW5lDQoNCkkgYmVsaWV2ZSB0aGF0IHRpa3ogZGlhZ3JhbXMgY2FuIGJlIGluY2x1ZGVkIGluIFJtYXJrZG93biBkb2N1bWVudHMgdXNpbmcgdGhlIHRpa3ogY29kZSBjaHVuayBlbmdpbmUgZm9yIG91dHB1dCB0eXBlcyBgcGRmX2RvY3VtZW50YCBhbmQgYGh0bWxfZG9jdW1lbnRgLiANCg0KVGlreiBpcyBhIHBhY2thZ2UgZm9yIExhVGVYIGFuZCBzbyBwZGYgaXMgaXRzIG5hdHVyYWwgb3V0cHV0LiBTbyBmYXIgSSBoYXZlIG5vdCBiZWVuIGFibGUgdG8gaW5jbHVkZSB0aGVzZSBmaWd1cmVzIGluIGVpdGhlciBgaHRtbF9ub3RlYm9va2Agb3IgYHdvcmRfZG9jdW1lbnRgIG91dHB1dCBmb3JtYXRzLiBJIHN1c3BlY3QgdGhhdCB0aGUgaW1hZ2VzIG5lZWQgY29udmVyc2lvbiB0byBwbmcgZmlsZXMgZm9yIHRoZXNlIHRvIHdvcmsgdW5kZXIgcm1hcmtkb3duLg0KDQpXZSB3aWxsIHVzZSBzb21lIG9mIHRoZSBMYVRlWCBjb2RlIGZvciBkcmF3aW5nIERBR3MgdXNpbmcgdGlreiBwcm92aWRlZCBieSBFbGVhbm9yIE11cnJheSBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2VsZWFub3JtdXJyYXkvY2F1c2FsZ3JhcGhzX2xhdGV4KS4NCg0KSW4gaXRzIExhVGVYIHByZWFtYmxlLCB0aGlzIGNvZGUgbG9hZHMgaW4gc2V2ZXJhbCB0aWt6IGxpYnJhcmllcy4gQXMgc3VjaCBmb3Igb3VyIHRpa3ogY29kZSBjaHVua3MgdG8gY29tcGlsZSBpbiBvdXIgUm1kIGZpbGUgd2UgbmVlZCB0byBkZWZpbmUgYSBuZXcgdGVtcGxhdGUgZmlsZSwgZS5nLmB0aWt6LXRlbXBsYXRlLnRleGAsIGFuZCBjYWxsIG91ciB0aWt6IGNvZGUgY2h1bmtzIGFzIGZvbGxvd3MuDQoNCiAgICBgciAnJ2BgYGB7dGlreiwgZmlnLmV4dD0nc3ZnJywgZmlnLndpZHRoPTMsIGVuZ2luZS5vcHRzPWxpc3QodGVtcGxhdGUgPSAidGlrei10ZW1wbGF0ZS50ZXgiKX0NCiAgICBcYmVnaW57dGlrenBpY3R1cmV9DQogICAgLi4uDQogICAgXGVuZHt0aWt6cGljdHVyZX0NCiAgICBgYGANCg0KTm90ZSB0aGF0IEkgb25seSBpbmNsdWRlIHRoZSBgZmlnLmV4dD0nc3ZnJ2AgY2h1bmsgb3B0aW9uIGZvciBodG1sIG91dHB1dC4gVGhlIGB0aWt6LXRlbXBsYXRlLnRleGAgZmlsZSBpbmNsdWRlcyB0aGUgdGlreiBwcmVhbWJsZSBjb2RlIGZyb20gRWxlYW5vciBNdXJyYXkncyBMYVRlWCBjb2RlIHBsdXMgdGhlIHJlcXVpcmVkIHRlbXBsYXRlIGNvZGUuIFRoZSBjb250ZW50cyBvZiB0aGUgdGVtcGxhdGUgZmlsZSBpcyBhcyBmb2xsb3dzLg0KDQpgYGB7YmFzaCBlY2hvPUZBTFNFLCBjb21tZW50PScnfQ0KY2F0IHRpa3otdGVtcGxhdGUudGV4DQpgYGANCg0KV2UgY2FuIG5vdyBwbG90IG91ciBEQUdzLg0KDQpBc2lkZTogSSBzaG91bGQgbm90ZSB0aGF0IEkgY291bGQgbm90IGdldCB0aGlzIGNvZGUgdG8gcnVuIG9uIG15IE1hY0Jvb2suIFRoaXMgaXMgYmVjYXVzZSBieSBkZWZhdWx0IHRoZSBgZHZpc3ZnYCBleGVjdXRhYmxlIG9uIE1hY09TIGFwcGVhcnMgdG8gYmUgbWlzc2luZyBgbGliZ3NgIHN1cHBvcnQuIEl0IGlzIHBvc3NpYmxlIHRvIGNvbXBpbGUgYGR2aXN2Z2AgZnJvbSBzb3VyY2UgaW5jbHVkaW5nIGBsaWJnc2Agc3VwcG9ydCBidXQgSSBkaWQgbm90IHRyeSB0aGlzLg0KDQojIyBTaW5nbGUgZ2Vub3R5cGUgREFHIHVzaW5nIHRpa3oNCg0KYGBge3Rpa3osIGZpZy5leHQ9J3N2ZycsIGZpZy53aWR0aD0zLCBlbmdpbmUub3B0cz1saXN0KHRlbXBsYXRlID0gInRpa3otdGVtcGxhdGUudGV4Iil9DQpcYmVnaW57dGlrenBpY3R1cmV9DQpcbm9kZSAoMSkgYXQgKDAsMCkge0d9Ow0KXG5vZGUgKDIpIGF0ICgxLjUsMCkge1h9Ow0KXG5vZGUgKDMpIGF0ICgzLDApIHtZfTsNClxub2RlICg0KSBhdCAoMi4yNSwxKSB7VX07DQpcZHJhd1tBcnJvd10gKDEuZWFzdCkgLS0gKDIud2VzdCk7DQpcZHJhd1tBcnJvd10gKDIuZWFzdCkgLS0gKDMud2VzdCk7DQpcZHJhd1tBcnJvd10gKDQpIC0tICgyKTsNClxkcmF3W0Fycm93XSAoNCkgLS0gKDMpOw0KXGVuZHt0aWt6cGljdHVyZX0NCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoPTI4OCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJ0aWt6LXNpbmdsZS5zdmciKQ0KYGBgDQoNCiMjIE11bHRpcGxlIGdlbm90eXBlIERBRyB1c2luZyB0aWt6DQoNCmBgYHt0aWt6LCBmaWcuZXh0PSdzdmcnLCBmaWcud2lkdGg9MywgZW5naW5lLm9wdHM9bGlzdCh0ZW1wbGF0ZSA9ICJ0aWt6LXRlbXBsYXRlLnRleCIpfQ0KXGJlZ2lue3Rpa3pwaWN0dXJlfQ0KXG5vZGUgKDEpIGF0ICgwLDApIHskR18yJH07DQpcbm9kZSAoMikgYXQgKDEuNSwwKSB7WH07DQpcbm9kZSAoMykgYXQgKDMsMCkge1l9Ow0KXG5vZGUgKDQpIGF0ICgyLjI1LDEpIHtVfTsNClxub2RlICg1KSBhdCAoMCwwLjUpIHskR18xJH07DQpcbm9kZSAoNikgYXQgKDAsLTAuNSkgeyRHXzMkfTsNClxkcmF3W0Fycm93XSAoMS5lYXN0KSAtLSAoMi53ZXN0KTsNClxkcmF3W0Fycm93XSAoMi5lYXN0KSAtLSAoMy53ZXN0KTsNClxkcmF3W0Fycm93XSAoNCkgLS0gKDIpOw0KXGRyYXdbQXJyb3ddICg0KSAtLSAoMyk7DQpcZHJhd1tBcnJvd10gKDUpIC0tICgyKTsNClxkcmF3W0Fycm93XSAoNikgLS0gKDIpOw0KXGVuZHt0aWt6cGljdHVyZX0NCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoPTI4OCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJ0aWt6LW11bHRpcGxlLnN2ZyIpDQpgYGANCg0KQWdhaW4gdGhpcyBjb3VsZCBiZSBleHRlbmRlZCBmb3IgbW9yZSBnZW5vdHlwZXMuDQoNCiMgcXVpY2tkYWcgcGFja2FnZQ0KDQpXZSBjYW4gcHJvZHVjZSBzb21lIG5pY2UgcGxvdHMgd2l0aCB0aGUgcXVpY2tkYWcgcGFja2FnZSwgd2hpY2ggYXBwZWFycyB0byBiZSBiYXNlZCBvbiBFbGVhbm9yIE11cnJheSdzIFRpa1ogY29kZSwgZnJvbSBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2pyZ2FudC9xdWlja2RhZykNCg0KYGBge3J9DQojIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJqcmdhbnQvcXVpY2tkYWciKQ0KDQpsaWJyYXJ5KHF1aWNrZGFnKQ0KDQplZGdlcyA8LSBjKCJHIC0+IFgiLA0KICAgICAgICAgICAiWCAtPiBZIiwNCiAgICAgICAgICAgIlUgLT4ge1ggWX0iKQ0KDQojIERBRw0KZGFnICA8LSBxZF9kYWcoZWRnZXMpDQpkYWcgJT4lIHJlbmRlcl9ncmFwaCgpDQpgYGANCg0KV2UgY2FuIGFsc28gZHJhdyB0aGUgc2luZ2xlIHdvcmxkIGludGVydmVudGlvbiBncmFwaCAoU1dJRykgZm9yIHRoZSBJViBtb2RlbCBhcyBmb2xsb3cuDQpgYGB7cn0NCiMgU1dpRw0Kc3dpZyA8LSBkYWcgJT4lDQogIHFkX3N3aWcoZml4ZWQubm9kZXMgPSBjKCJHIiwiWCIpLA0KICAgICAgICAgIGN1c3RvbS5sYWJlbHMgPSBjKCJHIiA9ICJnIiwgIlgiID0gIngiKSkNCnN3aWcgJT4lIHJlbmRlcl9ncmFwaCgpDQpgYGANCg0KIyBUaG9tYXMgUmljaGFyZHNvbidzIFRpa1ogY29kZSBmb3IgU1dJR3MNCg0KVGhvbWFzIFJpY2hhcmRzb24gcHJvdmlkZXMgYSBUaWtaL1BHRiBzaGFwZSBsaWJyYXJ5IGZvciBTV0lHcy4NCg0KKiBUaGUgYHBnZmxpYnJhcnlzaGFwZXMuc3dpZ3MuY29kZS50ZXhgIGZpbGUgaXMgW2hlcmVdKGh0dHBzOi8vc2l0ZXMuc3RhdC53YXNoaW5ndG9uLmVkdS90c3Ivd2Vic2l0ZS9kb2N1bWVudHMvMjAxOC90aWt6LWZvci1zd2lncy9wZ2ZsaWJyYXJ5c2hhcGVzLnN3aWdzLmNvZGUudGV4KS4gU2F2ZSBpdCBpbiB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5Lg0KDQoqIEFuIGFydGljbGUgZGVzY3JpYmluZyBpdHMgdXNlIGlzIFtoZXJlXShodHRwczovL3NpdGVzLnN0YXQud2FzaGluZ3Rvbi5lZHUvdHNyL3dlYnNpdGUvZG9jdW1lbnRzLzIwMTgvdGlrei1mb3Itc3dpZ3Mvc3dpZy1leGFtcGxlcy5wZGYpLg0KDQpUbyB1c2UgdGhlIHRpa3ogY2h1bmsgZW5naW5lIHdlIGFnYWluIHdyaXRlIGEgTGFUZVggdGVtcGxhdGUgYmFzZWQgb24gdGhlIHByZWFtYmxlIGluIHRoZSBhcnRpY2xlIGJ5IFJpY2hhcmRzb24uIFRoZSBMYVRlWCB0ZW1wbGF0ZSBpcyBhcyBmb2xsb3dzLg0KYGBge2Jhc2ggZWNobz1GQUxTRSwgY29tbWVudD0nJ30NCmNhdCB0aWt6LXRlbXBsYXRlLXN3aWctdHIudGV4DQpgYGANCg0KV2Ugd2lsbCByZWNyZWF0ZSB0aGUgU1dJRyB0ZW1wbGF0ZSBpbiBGaWd1cmUgMiAoYikgb2YgU3dhbnNvbiBldCBhbC4gW2hlcmVdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDgwLzAxNjIxNDU5LjIwMTguMTQzNDUzMCkuDQoNCmBgYHt0aWt6LCBmaWcuZXh0PSdzdmcnLCBmaWcud2lkdGg9MywgZW5naW5lLm9wdHM9bGlzdCh0ZW1wbGF0ZSA9ICJ0aWt6LXRlbXBsYXRlLXN3aWctdHIudGV4Iil9DQpcYmVnaW57dGlrenBpY3R1cmV9DQpcdGlrenNldHtsaW5lIHdpZHRoPTEuNXB0LCBvdXRlciBzZXA9MHB0LA0KICBlbGwvLnN0eWxlPXtkcmF3LGZpbGw9d2hpdGUsIGlubmVyIHNlcD0ycHQsDQogIGxpbmUgd2lkdGg9MS41cHR9LA0KICBzd2lnIHZzcGxpdD17Z2FwPTVwdCwgaW5uZXIgbGluZSB3aWR0aCByaWdodD0wLjVwdH0sDQogIHN3aWcgaHNwbGl0PXtnYXA9NXB0LCBpbm5lciBsaW5lIHdpZHRoIGxvd2VyPTAuNXB0fX07DQoNClxub2RlW25hbWU9RyxzaGFwZT1zd2lnIHZzcGxpdF17DQpcbm9kZXBhcnR7bGVmdH17JEckfQ0KXG5vZGVwYXJ0e3JpZ2h0fXskZyR9fTsNCg0KXG5vZGVbbmFtZT1YLCByaWdodD0xMG1tIG9mIEcsDQpzaGFwZT1zd2lnIGhzcGxpdF17DQpcbm9kZXBhcnR7dXBwZXJ9eyRYXmckfQ0KXG5vZGVwYXJ0e2xvd2VyfXskeCR9fTsNCg0KXG5vZGVbbmFtZT1VLCBlbGwsIHNoYXBlPWVsbGlwc2UsIGFib3ZlIHJpZ2h0PTcuNW1tIGFuZCA1bW0gb2YgWCwgZmlsbD1ncmF5XXskVSR9Ow0KDQpcbm9kZVtuYW1lPVksIGVsbCwgc2hhcGU9ZWxsaXBzZSwgcmlnaHQ9MTBtbSBvZiBYXXskWV54JH07DQoNClxkcmF3Wy0+LCBsaW5lIHdpZHRoPTEuNXB0LD49c3RlYWx0aF0NCiAgKEcpIGVkZ2Vbb3V0PTM2MCwgaW49MTcwXSAoWCkNCiAgKFUpIGVkZ2UgKFgpDQogIChVKSBlZGdlIChZKQ0KICAoWCkgdG9bb3V0PTMzMCwgaW49MTgwXSAoWSk7DQpcZW5ke3Rpa3pwaWN0dXJlfQ0KYGBgDQo=